package com.sslite.reports.dailyvolumeaggregatereport;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import com.sslite.dao.SingletonLostReportDataDao;
import com.sslite.model.ChartApplet;
import com.sslite.model.DateVolumeClose;
import com.sslite.model.IntraDayStructure;
import com.sslite.model.MetaData;
import com.sslite.util.ReportUtil;
import com.sslite.util.SSLiteProperties;
import com.sslite.util.Utility;

public class DailyVolumeAggregateReport implements Runnable{
	private MetaData metaData;
	private ReportUtil reportUtil;
	
	public DailyVolumeAggregateReport(MetaData metaData) {
		this.metaData = metaData;
		this.reportUtil = new ReportUtil(metaData);
	}

	public MetaData getMetaData() {
		return metaData;
	}

	public void setMetaData(MetaData metaData) {
		this.metaData = metaData;
	}
	
	@Override
	public void run(){
		try{
			generateReport();
		}
		catch(Exception e){
			System.out.println( "DailyVolumeAggregateReport: Exception while generating report for " +this.getMetaData().getSymbol() );
			e.printStackTrace();
		}
	}
	
	private void generateReport() throws Exception{
		
		final Map<Date, List<IntraDayStructure>> mapDateIds = reportUtil.getMapDateIds();
		final List<DateVolumeClose> aggregateDVC = getAggregateDVC(mapDateIds);
		
		final StringBuilder sbDates = new StringBuilder();
		final List<Long> volumeList = new ArrayList<Long>();
		final StringBuilder sbCloseData = new StringBuilder();
		
		String noteMaxVolxClose = "";
		Double maxVxC = 0.0;
		Long maxVolume = 0L, sumOfVolume = 0L;
		final Set<Date> datesToGenerateSnapshotsFor = new HashSet<Date>();
		Date dateWithMaxVxC = null;
		for( final DateVolumeClose dvc : aggregateDVC ){
			sbDates.append(",").append(Utility.getStrDate(dvc.getDate()));
			volumeList.add( dvc.getVolume() );
			sbCloseData.append(",").append( dvc.getClose() );

			sumOfVolume += dvc.getVolume();
			//
			if( Math.abs(dvc.getVolume()) > Math.abs(maxVolume) ){
				maxVolume = dvc.getVolume();
				maxVxC = (maxVolume * dvc.getClose());
				final Number mktCap = (metaData.getSharesOutstanding() != null ? (metaData.getSharesOutstanding()*dvc.getClose()) : null);
				noteMaxVolxClose = "Note: V x C on " +Utility.getStrDate(dvc.getDate())+ " was $" +Utility.getFormattedInteger(maxVxC)+ ". MktCap: " +Utility.getFormattedInteger(mktCap);
				if( mktCap != null ){
					noteMaxVolxClose += ". Best if (MktCap/maxVxC) less than 25 -> " +Utility.round(mktCap.doubleValue()/Math.abs(maxVxC));
				}
				if( maxVxC < 0 ){
					noteMaxVolxClose = "<font color=red>" +noteMaxVolxClose+ "</font>";
				}
				dateWithMaxVxC = dvc.getDate();
			}
		}
		
		if( dateWithMaxVxC != null ){
			datesToGenerateSnapshotsFor.add( dateWithMaxVxC );
		}
		
		final Double avgVolume = new Double(sumOfVolume) / new Double(volumeList.size());

		// *********** Start: Sub List (Qualification check based on past 20 days data) ***************** //
		final int DURATION_DAYS = 20;
		Long subMaxVolume = 0L;
		Double subAvgVolume = 0.0;
		if( aggregateDVC.size() > DURATION_DAYS ){
			final List<DateVolumeClose> subAggregateDVCList = aggregateDVC.subList(aggregateDVC.size() - DURATION_DAYS, aggregateDVC.size());
			Long sumOfSubVolume = 0L;
			for( DateVolumeClose dvc : subAggregateDVCList ){
				sumOfSubVolume += dvc.getVolume();
				if( Math.abs(dvc.getVolume()) > Math.abs(subMaxVolume) ){
					subMaxVolume = Math.abs(dvc.getVolume());
					
					dateWithMaxVxC = dvc.getDate();
				}
			}
			subAvgVolume = new Double(sumOfSubVolume) / new Double(subAggregateDVCList.size());
		}

		if( dateWithMaxVxC != null ){
			datesToGenerateSnapshotsFor.add( dateWithMaxVxC );
		}
		// *********** End: Sub List ***************** //

		// Add logic to include existing report data that I lost
		String lostData_CloseData = SingletonLostReportDataDao.getInstance().getProperty( metaData.getSymbol()+".sampleValues_0" );
		String lostData_VolumeData = SingletonLostReportDataDao.getInstance().getProperty( metaData.getSymbol()+".sampleValues_1" );
		String lostData_DatesData = SingletonLostReportDataDao.getInstance().getProperty( metaData.getSymbol()+".sampleLabels" );
		
		StringBuilder closeLostData = new StringBuilder();
		StringBuilder volumeLostData = new StringBuilder();
		StringBuilder datesLostData = new StringBuilder();
		
		if( lostData_DatesData != null && !lostData_DatesData.trim().equals("") ){
			final String dataAvailableFrom = sbDates.substring(1, sbDates.indexOf(",", 1));
			String[] arrCloseData = lostData_CloseData.split("[,]");
			String[] arrVolumeData = lostData_VolumeData.split("[,]");
			String[] arrDatesData = lostData_DatesData.split("[,]");
			
			for( int i=0; i<arrDatesData.length; i++ ){
				if( arrDatesData[i].equals( dataAvailableFrom ) ){
					break;
				}else{
					closeLostData.append( arrCloseData[i] ).append( "," );
					volumeLostData.append( arrVolumeData[i] ).append( "," );
					datesLostData.append( arrDatesData[i] ).append( "," );
				}
			}
			
		}

		final ChartApplet chartApplet = new ChartApplet( metaData.getSymbol(), 900, 350 );
		chartApplet.setParamValue("chartTitle", metaData.getSymbol());
		chartApplet.setParamValue("sampleValues_0", closeLostData + sbCloseData.substring(1));
		chartApplet.setParamValue("sampleValues_1", volumeLostData+ volumeList.toString().replaceAll("\\[|\\]", "").replaceAll(" ", ""));
		chartApplet.setParamValue("sampleLabels", datesLostData + sbDates.substring(1));

		final StringBuilder sbTmp = new StringBuilder();
		sbTmp.append( "<a href='http://www.google.com/finance?q=" +metaData.getSymbol()+ "' target='_new' " +(metaData.isInSogotradeShortList() ? "style='background-color:#00FF00'" : "")+ ">" +metaData.getSymbol()+ "</a> " );
		sbTmp.append( "<input type=button value='Show Intra Day Details' onclick=\"showIntraDayDetails('" +metaData.getSymbol()+ "')\"><BR>" );
		sbTmp.append( noteMaxVolxClose +" \n" );
		sbTmp.append( chartApplet.getChartAppletHTML() +"\n\n" );

		// Checks for Qualified only.
		boolean bQualified = false;
		if( Math.abs(maxVxC) >= Double.parseDouble( SSLiteProperties.QUALIFICATION_MAX_VXC_GREATER_THAN.getValue())
			&& ( 
				Math.abs(maxVolume) >= (avgVolume * Double.parseDouble(SSLiteProperties.QUALIFICATION_MAX_VOL_TIMES_OF_AVERAGE_VOL.getValue()) ) ||
				Math.abs(subMaxVolume) >= (subAvgVolume * Double.parseDouble(SSLiteProperties.QUALIFICATION_MAX_VOL_TIMES_OF_AVERAGE_VOL.getValue()) )
			)){
			
			if( metaData.getSharesOutstanding() != null ){
				Double mktCap = metaData.getSharesOutstanding() * (maxVxC / maxVolume);
				if( Math.abs(maxVxC) >= (mktCap / 30.0) || Math.abs(maxVxC) >= 1000000000.00 ){ // Added OR condition --> if maxVxC >= 1B
					bQualified = true;
				}
			}else{
				bQualified = true;
			}
		}
		
		if( bQualified ){
			Utility.saveContent(SSLiteProperties.TMP_RPT_FOLDER_DAILYVOLUMEAGGREGATE.getValue()+"q_"+metaData.getSymbol()+".html", sbTmp.toString());
		}else{
			Utility.saveContent(SSLiteProperties.TMP_RPT_FOLDER_DAILYVOLUMEAGGREGATE.getValue()+"uq_"+metaData.getSymbol()+".html", sbTmp.toString());
		}
		
		// Generate Snapshots
		for( Date d : datesToGenerateSnapshotsFor ){
			reportUtil.generateSnapshotFor(d);
		}
	}

	private List<DateVolumeClose> getAggregateDVC(final Map<Date, List<IntraDayStructure>> mapDateIds) throws Exception{
		final List<DateVolumeClose> aggregateDVC = new ArrayList<DateVolumeClose>();
		final Set<Map.Entry<Date, List<IntraDayStructure>>> set = mapDateIds.entrySet();
		for( final Map.Entry<Date, List<IntraDayStructure>> me : set ){
			final Date date = me.getKey();
			final List<IntraDayStructure> idsList = me.getValue();
			//
			Long bVol = 0L;
			Long sVol = 0L;
			for(int i=0; i<idsList.size(); i++){
				final IntraDayStructure ids = idsList.get(i);
				IntraDayStructure prev = null;
				
				if( i == 0){
					final Map.Entry<Date, List<IntraDayStructure>> me1 = ((TreeMap) mapDateIds).lowerEntry( date );
					if( me1 != null ){
						List<IntraDayStructure> prevIdsList = me1.getValue();
						prev = prevIdsList.get( prevIdsList.size()-1 );
					}else{
						prev = idsList.get(i);
					}
				}else{
					prev = idsList.get( i-1 );
				}

				Double prevClose = prev.getClose();

				// Simple Logic: If close >= prevClose then consider BUY otherwise SELL.
				if( ids.getClose() >= prevClose ){
					bVol += ids.getVolume();
				}else if( ids.getClose() < prevClose ){
					sVol += ids.getVolume();
				}
			}

			final Double dayClose = idsList.get(idsList.size()-1).getClose();
			final Long vol = bVol-sVol;

			Date timeAgnosticDate = Utility.getDate( Utility.getStrDate(date) ); // To strip-off time
			aggregateDVC.add( new DateVolumeClose( timeAgnosticDate, vol, dayClose ) );
		}
		return aggregateDVC;
	}
}
